package com.weilive.core.utils;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashMap;

import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;

public class LinuxUtils
{
	public static final int CONSERVATIVE    = 0;
	public static final int AVERAGE     = 1;
	public static final int OPTIMISTIC  = 2;

	public static String exec(String command) {
		BufferedReader reader = null;
		try {
			Process process = Runtime.getRuntime().exec(command);
			reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
			StringBuffer ret = new StringBuffer();
			String line;
			while ((line = reader.readLine()) != null) {
				ret.append(line + "\r\n");
			}
			reader.close();
			return ret.toString();
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (reader != null) try { reader.close(); } catch (Exception e) { }
		}
		return "";
	}

	public static String remoteExec(String ip, String name, String password, String command) {
		Connection connection = null;
		Session session = null;
		BufferedReader reader = null;
		try {
			connection = new Connection(ip);  
			connection.connect();
			boolean isAuthenticated = connection.authenticateWithPassword(name, password);  
			if (isAuthenticated) {
				session = connection.openSession();  
				session.execCommand(command);  
				InputStream stdout = new StreamGobbler(session.getStdout());  
				reader = new BufferedReader(new InputStreamReader(stdout));
				StringBuffer ret = new StringBuffer();
				String line;
				while ((line = reader.readLine()) != null) {
					ret.append(line + "\r\n");
				}
				return ret.toString();
			} else {
				System.out.printf("ssh[%s@%s]: auth failed!", name, ip);
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			if (reader != null) try { reader.close(); } catch (Exception e) { }
			if (session != null) try { session.close(); } catch (Exception e) { }
			if (connection != null) try { connection.close(); } catch (Exception e) { }
		}
		return "";
	}

	/**
	 * cpuUsage gives us the percentage of cpu usage
	 * 
	 * mpstat -P ALL out stream example:
	 *
	 *  Linux 3.2.0-30-generic (castarco-laptop)    10/09/12    _x86_64_    (2 CPU)                 - To discard
	 *                                                                                              - To discard
	 *  00:16:30     CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest   %idle    - To discard
	 *  00:16:30     all   17,62    0,03    3,55    0,84    0,00    0,03    0,00    0,00   77,93
	 *  00:16:30       0   17,36    0,05    3,61    0,83    0,00    0,05    0,00    0,00   78,12
	 *  00:16:30       1   17,88    0,02    3,49    0,86    0,00    0,01    0,00    0,00   77,74
	 * 
	 * @param measureMode Indicates if we want optimistic, convervative or average measurements.
	 */
	public static Double cpuUsage (int measureMode) throws Exception {

		BufferedReader mpstatReader = null;

		String      mpstatLine;
		String[]    mpstatChunkedLine;
		Double      selected_idle;

		try {
			Runtime runtime = Runtime.getRuntime();
			Process mpstatProcess = runtime.exec("mpstat -P ALL");

			mpstatReader = new BufferedReader(new InputStreamReader(mpstatProcess.getInputStream()));

			// We discard the three first lines
			mpstatReader.readLine();
			mpstatReader.readLine();
			mpstatReader.readLine();

			mpstatLine = mpstatReader.readLine();
			if (mpstatLine == null) {
				throw new Exception("mpstat didn't work well");
			} else if (measureMode == LinuxUtils.AVERAGE) {
				mpstatChunkedLine = mpstatLine.replaceAll(",", ".").split("\\s+");
				selected_idle = Double.parseDouble(mpstatChunkedLine[10]);
			} else {
				selected_idle   = (measureMode == LinuxUtils.CONSERVATIVE)?200.:0.;
				Double candidate_idle;

				int i = 0;
				while((mpstatLine = mpstatReader.readLine()) != null) {
					mpstatChunkedLine = mpstatLine.replaceAll(",", ".").split("\\s+");
					candidate_idle = Double.parseDouble(mpstatChunkedLine[10]);

					if (measureMode == LinuxUtils.CONSERVATIVE) {
						selected_idle = (selected_idle < candidate_idle)?selected_idle:candidate_idle;
					} else if (measureMode == LinuxUtils.OPTIMISTIC) {
						selected_idle = (selected_idle > candidate_idle)?selected_idle:candidate_idle;
					}
					++i;
				}
				if (i == 0) {
					throw new Exception("mpstat didn't work well");
				}
			}
		} catch (Exception e) {
			throw e; // It's not desirable to handle the exception here
		} finally {
			if (mpstatReader != null) try { mpstatReader.close(); } catch (IOException e) { }
		}

		return  100-selected_idle;
	}

	/**
	 * memoryUsage gives us data about memory usage (RAM and SWAP)
	 */
	public static HashMap<String, Integer> memoryUsage () throws Exception {
		BufferedReader freeReader = null;

		String      freeLine;
		String[]    freeChunkedLine;

		HashMap<String, Integer> usageData = new HashMap<String, Integer>();

		try {
			Runtime runtime = Runtime.getRuntime();
			Process freeProcess = runtime.exec("free -k"); // We measure memory in kilobytes to obtain a greater granularity

			freeReader = new BufferedReader(new InputStreamReader(freeProcess.getInputStream()));

			// We discard the first line
			freeReader.readLine();

			freeLine = freeReader.readLine();
			if (freeLine == null) {
				throw new Exception("free didn't work well");
			}
			freeChunkedLine = freeLine.split("\\s+");

			usageData.put("total", Integer.parseInt(freeChunkedLine[1]));

			freeLine = freeReader.readLine();
			if (freeLine == null) {
				throw new Exception("free didn't work well");
			}
			freeChunkedLine = freeLine.split("\\s+");

			usageData.put("used", Integer.parseInt(freeChunkedLine[2]));

			freeLine = freeReader.readLine();
			if (freeLine == null) {
				throw new Exception("free didn't work well");
			}
			freeChunkedLine = freeLine.split("\\s+");

			usageData.put("swap_total", Integer.parseInt(freeChunkedLine[1]));
			usageData.put("swap_used", Integer.parseInt(freeChunkedLine[2]));
		} catch (Exception e) {
			throw e;
		} finally {
			if (freeReader != null) try { freeReader.close(); } catch (IOException e) { }
		}
		return usageData;
	}
}
